package org.supsi.skarabee.bt;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.supsi.skarabee.model.NodeModel;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.ParcelUuid;
import android.view.View;

public class BTConnnectionManager{


	private static BTConnnectionManager instance = null;
	private String macAddress;
	private int port;
	private ConnectThread connectThread;
	private ConnectedThread connectedThread;
	private BluetoothDevice mDevice;
	private BluetoothAdapter mBluetoothAdapter = null;

	private BTConnnectionManager(String macAddress, int port, BluetoothAdapter btAdapter) {
		super();
		this.macAddress = macAddress;
		this.port = port;
		this.mBluetoothAdapter = btAdapter;
	}


	public static BTConnnectionManager getInstance(String macAddress, int port, BluetoothAdapter btAdapter){
		if(instance == null){
			instance = new BTConnnectionManager(macAddress, port, btAdapter);
		}
		return instance;
	}




	public void startConnection() {
		//notifyMessage("log", "Starting connection...");
		mDevice = mBluetoothAdapter.getRemoteDevice(macAddress);
		try {
			connectThread = new ConnectThread(mDevice);
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		connectThread.start();
	}

	public void closeConnection(View view){
		//notifyMessage("log", "Closing connection...");
		connectedThread.cancel();
	}

	public void send(String s){
		connectedThread.write(s.getBytes());
	}


	private void manageConnectedSocket(BluetoothSocket socket){
		connectedThread = new ConnectedThread(socket);
		connectedThread.start();
	}



	private class ConnectThread extends Thread {
		private final BluetoothSocket mmSocket;
		private final BluetoothDevice mmDevice;
		public ConnectThread(BluetoothDevice device) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
			// Use a temporary object that is later assigned to mmSocket,
			// because mmSocket is final
			BluetoothSocket tmp = null;
			mmDevice = device;
			Method m = mmDevice.getClass().getMethod("createInsecureRfcommSocket", new Class[] {int.class});
			tmp = (BluetoothSocket) m.invoke(mmDevice,port);
			mmSocket = tmp;
		}
		public void run() {
			// Cancel discovery because it will slow down the connection
			mBluetoothAdapter.cancelDiscovery();

			try {
				// Connect the device through the socket. This will block
				// until it succeeds or throws an exception
				//notifyMessage("log", "Connecting to the socket...");
				mmSocket.connect();
				//notifyMessage("log", "Connected");

			} catch (IOException connectException) {
				ParcelUuid[] uuids = mmSocket.getRemoteDevice().getUuids();
				String uuid = uuids[0].getUuid().toString();
				//notifyMessage("log", "Can't connect to the socket, abort");
				//notifyMessage("log", uuid);
				// Unable to connect; close the socket and get out
				try {
					mmSocket.close();
				} catch (IOException closeException) { }
				return;
			}

			// Do work to manage the connection (in a separate thread)

			manageConnectedSocket(mmSocket);

		}

		/** Will cancel an in-progress connection, and close the socket */
		public void cancel() {
			try {
				mmSocket.close();
			} catch (IOException e) { }
		}
	}



	private class ConnectedThread extends Thread {
		private final BluetoothSocket mmSocket;
		private final InputStream mmInStream;
		private final OutputStream mmOutStream;
		//private Handler handler;

		public ConnectedThread(BluetoothSocket socket) {
			//this.handler = handler;
			mmSocket = socket;
			InputStream tmpIn = null;
			OutputStream tmpOut = null;

			// Get the input and output streams, using temp objects because
			// member streams are final
			try {
				tmpIn = socket.getInputStream();
				tmpOut = socket.getOutputStream();
			} catch (IOException e) { 
			}
			mmInStream = tmpIn;
			mmOutStream = tmpOut;
		}

		public void run() {
			byte[] buffer = new byte[1024];  // buffer store for the stream
			int bytes; // bytes returned from read()
			String message = "";
			// Keep listening to the InputStream until an exception occurs
			while (true) {
				try {
					buffer = new byte[1024];
					// Read from the InputStream
					bytes = mmInStream.read(buffer);

					// Send the obtained bytes to the UI activity
					String str = new String(buffer);
					message += str;

					if(message.contains("!")){
						NodeModel.getInstance().setJson(message);
						message = "";
					}
				} catch (IOException e) {
					e.printStackTrace();
					break;
				}
			}
		}

		/* Call this from the main activity to send data to the remote device */
		public void write(byte[] bytes) {
			try {
				mmOutStream.write(bytes);
			} catch (IOException e) { 
				e.printStackTrace();
			}
		}

		/* Call this from the main activity to shutdown the connection */
		public void cancel() {
			try {
				mmSocket.close();
			} catch (IOException e) { 
				e.printStackTrace();
			}
		}

	}




}
